`timescale 1ns / 1ps

module adder_tree_2 #(
    parameter NUM_INPUTS = 32,
    parameter DATA_WIDTH = 40,
    parameter OUT_WIDTH  = 48          // 输出累加宽度
)(
    input  wire                         clk,
    input  wire                         rst_n,
    input  wire [NUM_INPUTS*DATA_WIDTH-1:0] data,
    output reg  [OUT_WIDTH-1:0]         sum_out
);
    // Stage1: 32 -> 16
    wire signed [OUT_WIDTH-1:0] s1 [0:15];
    genvar i;
    generate
        for(i=0;i<16;i=i+1) begin: G_L1
            wire signed [DATA_WIDTH-1:0] a = data[(2*i)*DATA_WIDTH +: DATA_WIDTH];
            wire signed [DATA_WIDTH-1:0] b = data[(2*i+1)*DATA_WIDTH +: DATA_WIDTH];
            assign s1[i] = a + b;
        end
    endgenerate
    reg signed [OUT_WIDTH-1:0] r1 [0:15];
    integer k1;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(k1=0;k1<16;k1=k1+1) r1[k1] <= 0;
        else
            for(k1=0;k1<16;k1=k1+1) r1[k1] <= s1[k1];
    end

    // Stage2: 16 -> 8
    wire signed [OUT_WIDTH-1:0] s2 [0:7];
    generate
        for(i=0;i<8;i=i+1) begin: G_L2
            assign s2[i] = r1[2*i] + r1[2*i+1];
        end
    endgenerate
    reg signed [OUT_WIDTH-1:0] r2 [0:7];
    integer k2;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(k2=0;k2<8;k2=k2+1) r2[k2] <= 0;
        else
            for(k2=0;k2<8;k2=k2+1) r2[k2] <= s2[k2];
    end

    // Stage3: 8 -> 4
    wire signed [OUT_WIDTH-1:0] s3 [0:3];
    generate
        for(i=0;i<4;i=i+1) begin: G_L3
            assign s3[i] = r2[2*i] + r2[2*i+1];
        end
    endgenerate
    reg signed [OUT_WIDTH-1:0] r3 [0:3];
    integer k3;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(k3=0;k3<4;k3=k3+1) r3[k3] <= 0;
        else
            for(k3=0;k3<4;k3=k3+1) r3[k3] <= s3[k3];
    end

    // Stage4: 4 -> 2
    wire signed [OUT_WIDTH-1:0] s4 [0:1];
    assign s4[0] = r3[0] + r3[1];
    assign s4[1] = r3[2] + r3[3];

    // Stage5: 2 -> 1
    wire signed [OUT_WIDTH-1:0] s5 = s4[0] + s4[1];

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            sum_out <= 0;
        else
            sum_out <= s5;
    end
endmodule